package com.zkt.recommend.domain.algorithm;

import com.zkt.recommend.common.constants.RecommendConstant;
import com.zkt.recommend.common.constants.RecommendationProperties;
import com.zkt.recommend.domain.redis.RedisUtil;
import com.zkt.recommend.domain.utils.DateUtil;
import com.zkt.recommend.infra.basic.entity.RecommendUserPortrait;
import com.zkt.recommend.infra.basic.entity.SubjectInfo;
import com.zkt.recommend.infra.rpc.SubjectRPC;
import com.zkt.subject.entity.SubjectInfoDTO;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author 赵开泰
 * @program jc-club
 * @date 2025/3/28
 * @description 推荐算法
 **/

@Component
public class RecommendationAlgorithm {
	
	@Resource
	private RedisUtil redisUtil;
	
	@Resource
	private RecommendationProperties properties;
	
	@Resource
	private SubjectRPC subjectRPC;
	
	/**
	 * 根据用户画像和题目列表推荐题目
	 *
	 * @param userPortraits 用户画像列表
	 * @param questions     题目列表
	 * @param count         推荐数量
	 * @return 推荐的题目列表
	 */
	public List<SubjectInfoDTO> recommend(List<RecommendUserPortrait> userPortraits, List<SubjectInfoDTO> questions, Integer count) {
		if (count == null || count <= 0) {
			count = properties.getRecommendNum();
		}
		
		if (userPortraits == null || userPortraits.isEmpty() || questions == null || questions.isEmpty()) {
			return getHotQuestions(count);
		}
		
		// 计算题目的综合评分
		Map<SubjectInfoDTO, Double> questionScores = new HashMap<>();
		for (RecommendUserPortrait portrait : userPortraits) {
			for (SubjectInfoDTO question : questions) {
				if (portrait.getLabelId() == null || question.getLabelIds().contains(portrait.getLabelId())) {
					double score = calculateScore(portrait, question);
					questionScores.put(question, questionScores.getOrDefault(question, 0.0) + score);
				}
			}
		}
		
		return questionScores.entrySet().stream()
				.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
				.limit(count)
				.map(Map.Entry::getKey)
				.collect(Collectors.toList());
	}
	
	/**
	 * 计算题目适应度评分
	 */
	private double calculateScore(RecommendUserPortrait portrait, SubjectInfoDTO question) {
		double score = 0.0;
		
		// 遗忘曲线影响
		double forgettingWeight = DateUtil.calculateForgettingWeight(portrait.getLastPracticeTime());
		
		// 计算综合评分
		score += (100 - portrait.getMasteryScore()) * properties.getMasteryScore();
		score += forgettingWeight * properties.getLastPracticeTime();
		score += portrait.getPracticeCount() * properties.getPracticeCount();
		score += portrait.getAvgDuration().multiply(BigDecimal.valueOf(properties.getAverageTime())).doubleValue();
		score += (BigDecimal.valueOf(100).subtract(portrait.getAvgScore())).multiply(BigDecimal.valueOf(properties.getAverageScoreRate())).doubleValue();
		// 适中难度优先
		Integer middleDifficulty = properties.getMiddleDifficulty();
		score += (middleDifficulty - Math.abs(middleDifficulty - question.getSubjectDifficult())) * properties.getDifficulty();
		
		return score;
	}
	
	/**
	 * 从 Redis 获取热点题目
	 *
	 * @param count 需要的题目数量
	 * @return 热点题目列表
	 */
	private List<SubjectInfoDTO> getHotQuestions(int count) {
		Set<Long> hotQuestions = redisUtil.rangeZset(RecommendConstant.HOT_QUESTIONS_KEY, 0, count - 1);
		if (hotQuestions == null || hotQuestions.isEmpty()) {
			return Collections.emptyList();
		}
		
		return subjectRPC.getSubjectsByIds(hotQuestions.stream().toList());
	}
	
}